﻿using System;
using System.CodeDom.Compiler;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Felbot.Interaction;
using Felbot.Interface.Conversations;
using Felbot.Scripting;

namespace Felbot {
    /// <summary>This form shows a log of script activity.</summary>
    public partial class frmScriptOutput : Form {
        private bool m_Terminate = false;

        /// <summary>Create a new script output form.</summary>
        public frmScriptOutput() {
            InitializeComponent();
        }

        private void frmScriptOutput_Load(object sender, EventArgs e) {
            rtbOutput.Initialize("Script Output");

            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Initialized!"));

            LoadScripts();
        }

        private void LoadScripts() {
            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Loading scripts.."));
            try {
                ScriptInfo[] scriptInfos = ScriptControl.LoadScripts(new DirectoryInfo(Program.ApplicationFolder + "\\Scripts"), "*.fbs");

                foreach (ScriptInfo scriptInfo in scriptInfos) {
                    if (scriptInfo.CompileResults.NativeCompilerReturnValue == 0) {
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] " + scriptInfo.ScriptName + " compiled successfully."));

                        rtbOutput.AddTextNodes(new TextNode(Color.Cyan, "   [ " + scriptInfo.ScriptName + " ]"));
                        rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Internal name: " + scriptInfo.ScriptInternalName));
                        rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Filepath: " + scriptInfo.ScriptPath));
                        rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Language: " + scriptInfo.ScriptLanguage));
                        rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Version: " + scriptInfo.ScriptVersion.ToString()));
                        if (scriptInfo.ScriptAuthor != null)
                            rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Author: " + scriptInfo.ScriptAuthor));
                        if (scriptInfo.ScriptDescription != null)
                            rtbOutput.AddTextNodes(new TextNode(Color.LightGreen, " Description: " + scriptInfo.ScriptDescription));
                    } else {
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] " + scriptInfo.ScriptName + " failed to compile."));
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "Filepath: " + scriptInfo.ScriptPath));
                    }

                    foreach (CompilerError compError in scriptInfo.CompileResults.Errors) {
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] A script compile error " + (compError.IsWarning ? "(warning) " : "") + "occured in " + scriptInfo.ScriptName));
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] (" + (compError.Line - 9) + "," + compError.Column + ") Error " + compError.ErrorNumber + ": " + compError.ErrorText));
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Line: "), new TextNode(Color.Gold, scriptInfo.SourceLines[compError.Line - 9]));
                    }
                }
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete. " + ScriptControl.ScriptCount + " scripts loaded."));
            } catch (DirectoryNotFoundException) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Script folder not found."));
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Creating folder.."));

                string directory = Program.ApplicationFolder + "\\Scripts";
                Directory.CreateDirectory(directory);
                LoadScripts();
                return;
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error loading scripts. " + e.ToString()));
            }

            CallFunction("onload");
        }

        public void ReloadScripts() {
            CallFunction("onunload");

            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Reloading scripts.."));
            try {
                ScriptControl.ReloadScripts();
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error reloading scripts. " + e.ToString()));
            }

            CallFunction("onload");
        }

        private void UnloadScripts() {
            CallFunction("onunload");

            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Unloading scripts.."));
            try {
                ScriptControl.UnloadScripts();
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error unloading scripts. " + e.ToString()));
            }
        }

        public ScriptFunctionReturn CallFunction(string scriptName, string functionName) {
            return CallFunction(scriptName, functionName, null);
        }

        public ScriptFunctionReturn CallFunction(string scriptName, string functionName, params object[] parameters) {
            try {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Calling " + scriptName + "." + functionName + "() in all scripts.."));
                CommandSource source = new CommandSource(CommandSourcePlace.Event, null, null);
                ScriptFunctionReturn scriptReturn = ScriptControl.CallFunction(source, scriptName, functionName, parameters);
                switch (scriptReturn.ReturnType) {
                    case ScriptFunctionReturnType.ReturnsVoid:
                        rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "returned void."));
                        break;

                    case ScriptFunctionReturnType.ReturnsObject:
                        rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "returned an object of type " +
                            scriptReturn.ReturnValue.GetType().Name + ": " +
                            scriptReturn.ReturnValue.ToString()));
                        break;

                    case ScriptFunctionReturnType.DoesNotExist:
                        //  rtbOutput.AddTextNodes(new TextNode(Color.Yellow,
                        //      "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                        //      "did not exist."));
                        break;

                    case ScriptFunctionReturnType.ExecutionException:
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "threw an exception: " + scriptReturn.Exception.ToString()));
                        break;
                }
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
                return scriptReturn;
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error calling function. " + e.ToString()));
                return null;
            }
        }

        public ScriptFunctionReturn[] CallFunction(string functionName) {
            return CallFunction(functionName, (object) null);
        }

        public ScriptFunctionReturn[] CallFunction(string functionName, params object[] parameters) {
            try {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Calling " + functionName + "() in all scripts.."));
                CommandSource source = new CommandSource(CommandSourcePlace.Event, null, null);
                ScriptFunctionReturn[] scriptReturns = ScriptControl.CallFunction(source, functionName, parameters);
                foreach (ScriptFunctionReturn scriptReturn in scriptReturns) {
                    switch (scriptReturn.ReturnType) {
                        case ScriptFunctionReturnType.ReturnsVoid:
                            rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "returned void."));
                            break;

                        case ScriptFunctionReturnType.ReturnsObject:
                            rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "returned an object of type " +
                                scriptReturn.ReturnValue.GetType().Name + ": " +
                                scriptReturn.ReturnValue.ToString()));
                            break;

                        case ScriptFunctionReturnType.DoesNotExist:
                            //  rtbOutput.AddTextNodes(new TextNode(Color.Yellow,
                            //      "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            //      "did not exist."));
                            break;

                        case ScriptFunctionReturnType.ExecutionException:
                            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "threw an exception: " + scriptReturn.Exception.ToString()));
                            break;
                    }
                }
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
                return scriptReturns;
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error calling function. " + e.ToString()));
                return null;
            }
        }

        public ScriptFunctionReturn CallFunction(CommandSource source, string scriptName, string functionName) {
            return CallFunction(source, scriptName, functionName, null);
        }

        public ScriptFunctionReturn CallFunction(CommandSource source, string scriptName, string functionName, params object[] parameters) {
            try {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Calling " + scriptName + "." + functionName + "() in all scripts.."));
                ScriptFunctionReturn scriptReturn = ScriptControl.CallFunction(source, scriptName, functionName, parameters);
                switch (scriptReturn.ReturnType) {
                    case ScriptFunctionReturnType.ReturnsVoid:
                        rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "returned void."));
                        break;

                    case ScriptFunctionReturnType.ReturnsObject:
                        rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "returned an object of type " +
                            scriptReturn.ReturnValue.GetType().Name + ": " +
                            scriptReturn.ReturnValue.ToString()));
                        break;

                    case ScriptFunctionReturnType.DoesNotExist:
                        //  rtbOutput.AddTextNodes(new TextNode(Color.Yellow,
                        //      "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                        //      "did not exist."));
                        break;

                    case ScriptFunctionReturnType.ExecutionException:
                        rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail,
                            "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            "threw an exception: " + scriptReturn.Exception.ToString()));
                        break;
                }
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
                return scriptReturn;
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error calling function. " + e.ToString()));
                return null;
            }
        }

        public ScriptFunctionReturn[] CallFunction(CommandSource source, string functionName) {
            return CallFunction(source, functionName, (object) null);
        }

        public ScriptFunctionReturn[] CallFunction(CommandSource source, string functionName, params object[] parameters) {
            try {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Try, "[ScriptControl] Calling " + functionName + "() in all scripts.."));
                ScriptFunctionReturn[] scriptReturns = ScriptControl.CallFunction(source, functionName, parameters);
                foreach (ScriptFunctionReturn scriptReturn in scriptReturns) {
                    switch (scriptReturn.ReturnType) {
                        case ScriptFunctionReturnType.ReturnsVoid:
                            rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "returned void."));
                            break;

                        case ScriptFunctionReturnType.ReturnsObject:
                            rtbOutput.AddTextNodes(new TextNode(Color.Cyan,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "returned an object of type " +
                                scriptReturn.ReturnValue.GetType().Name + ": " +
                                scriptReturn.ReturnValue.ToString()));
                            break;

                        case ScriptFunctionReturnType.DoesNotExist:
                            //  rtbOutput.AddTextNodes(new TextNode(Color.Yellow,
                            //      "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                            //      "did not exist."));
                            break;

                        case ScriptFunctionReturnType.ExecutionException:
                            rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail,
                                "[ScriptControl] " + scriptReturn.ScriptName + "." + functionName + "()" +
                                "threw an exception: " + scriptReturn.Exception.ToString()));
                            break;
                    }
                }
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Success, "[ScriptControl] Complete."));
                return scriptReturns;
            } catch (Exception e) {
                rtbOutput.AddTextNodes(new TextNode(FCSObjects.Chatroom_Bot_Fail, "[ScriptControl] Error calling function. " + e.ToString()));
                return null;
            }
        }

        private void frmScriptOutput_FormClosing(object sender, FormClosingEventArgs e) {
            if (!m_Terminate) {
                e.Cancel = true;
                this.Hide();
            }
        }

        internal void Terminate() {
            UnloadScripts();

            m_Terminate = true;
            this.Close();
        }

        private void mnuText_Opening(object sender, System.ComponentModel.CancelEventArgs e) {
            mnuTextCopy.Enabled = (rtbOutput.SelectionLength > 0);
            mnuTextDelete.Text = "Clear";
        }

        private void mnuTextCopy_Click(object sender, EventArgs e) {
            rtbOutput.Copy();
        }

        private void mnuTextDelete_Click(object sender, EventArgs e) {
            rtbOutput.Clear();
        }

        private void mnuTextSelectAll_Click(object sender, EventArgs e) {
            rtbOutput.SelectAll();
        }
    }
}
